﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Yuchen.NPA.Core.CompilerService.Method;

namespace Yuchen.NPA.Core
{
    public class MethodParser
    {
        public static readonly string[] CALCOPERATORS = new string[]
            {
                "NotEquals",
                "Between",
                "NotBetween",
                "LessThan",
                "LessThanEqual",
                "GreaterThan",
                "GreaterThanEqual",
                "After",
                "Before",
                "IsNull",
                "IsNotNull",
                "NotNull",
                "Like",
                "NotLike",
                "StartsWith",
                "NotStartsWith",
                "NotEndsWith",
                "EndsWith",
                "Contains",
                "NotContains",
                "In",
                "NotIn",
                "True",
                "False"
            }.OrderByDescending(x => x.Length)
            .ToArray();
        public static readonly string[] LOGICOPERATORS = new string[] { "And", "Or" }.OrderByDescending(x => x.Length).ToArray();
        public static readonly string[] CONCATORDERBYOPERATORS = new string[] { "And" }.OrderByDescending(x => x.Length).ToArray();
        public static readonly string[] EXTRAOPERATORS = new string[] { "IgnoreCase" };
        public static readonly string[] ORDERBYOPERATORS = new string[] { "OrderBy" };
        public static readonly string[] SORTDIRECTIONOPERATORS = new string[] { "Asc", "Descending" };

        public static readonly string[] OPERATORS = CALCOPERATORS.Union(LOGICOPERATORS).Union(ORDERBYOPERATORS).OrderByDescending(x => x.Length).ToArray();
        private string[] properties;
        private string fullText;
        private MethodDescription methodDescription;

        public MethodParser(IEnumerable<string> properties, string fullText)
        {
            this.fullText = fullText;
            this.properties = properties.OrderByDescending(x => x.Length).ToArray();
        }

        /// <summary>
        /// 如果总长度为1，那么i大于等于0时才是true
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        bool IsEOL(int i)
        {
            if (fullText.Length == i + 1)
            {
                return true;
            }
            else if (fullText.Length > i + 1)
            {
                return false;
            }
            throw new Exception("边界值错误");
        }

        public MethodDescription Parse(int position)
        {
            methodDescription = new MethodDescription();
            State currentState = State.Method;
            PropertyDescription propertyDescription = null;
            var propertyName = string.Empty;
            Token token = null;
            PropertyOrderBy orderBy = null;
            for (int i = 0; i < fullText.Length; i++)
            {
                var c = fullText[i];
                var prevChar = i > 1 ? fullText[i - 1] : '0';
                Token propertyToken;
                switch (currentState)
                {
                    case State.Method:
                        //整个解析开始，遇到这几个字符时，才是解析器需要处理的方法，如果不是，直接返回
                        if (c != 'F' && c != 'G' && c != 'R' && c != 'U' && c != 'D'
                            && c != 'f' && c != 'g' && c != 'r' && c != 'u' && c != 'd')
                        {
                            return methodDescription;
                        }
                        currentState = State.Prefix;
                        token = new Token()
                        {
                            Text = c.ToString(),
                            Type = State.Prefix,
                            BeginIndex = i
                        };
                        if (IsEOL(i))
                        {
                            token.IsCurrent = true;
                            methodDescription.Tokens.Add(token);
                            methodDescription.Prefix = token.Text;
                        }
                        break;
                    case State.Prefix:
                        token.Text += c.ToString();
                        methodDescription.Prefix = token.Text;
                        //遇到y且上一个字符为B也就是By时，属性名解析开始
                        if (c == 'y' && prevChar == 'B')
                        {
                            currentState = State.PropertyName;
                            token.IsCurrent = (position >= token.BeginIndex && position < i) && !IsEOL(position);
                            methodDescription.Tokens.Add(token);
                            token = new Token()
                            {
                                Type = State.PropertyName,
                                BeginIndex = i + 1,
                                Text = string.Empty
                            };
                            propertyDescription = new PropertyDescription();
                            if (IsEOL(i))
                            {
                                token.IsCurrent = position == i;
                                methodDescription.Tokens.Add(token);
                            }
                        }
                        else if (IsEOL(i))
                        {
                            token.IsCurrent = position >= token.BeginIndex - token.Text.Length;
                            methodDescription.Tokens.Add(token);
                        }
                        break;
                    case State.PropertyName:
                        token.Text += c.ToString();
                        //将当前字符串拿去属性名列表搜索，如果存在，则当前已扫描到一个属性名
                        if (properties.Contains(token.Text))
                        {
                            //但有可能这只是一部分属性名，所以需要主动往前继续搜索，搜索的长度为属性名的最大长度
                            //搜索过程中可能出现以下两种情况
                            //1、搜索到了结尾没有出现逻辑操作符，那么从当前到结尾的都可能是属性名
                            //2、搜索到了结尾出现了逻辑操作符，那么夹在中间的就是属性名
                            var operatorInfo = FindNextOperatorTokenUntilEnd(i);
                            var operatorText = operatorInfo?.Text;
                            var operatorIndex = operatorInfo?.BeginIndex ?? -1;
                            var property = token.Text;

                            if (string.IsNullOrWhiteSpace(operatorText))
                            {
                                //出现了第一种情况，此时需要再次从属性名结尾搜索到字符串结尾，搜索到的字符串去跟操作符集合匹配，如果有匹配，则识别为操作符
                                operatorIndex = token.BeginIndex + token.Text.Length;
                                operatorText = fullText.Substring(operatorIndex);
                                if (!OPERATORS.Any(x => x.StartsWith(operatorText)))
                                {
                                    operatorText = string.Empty;
                                    operatorIndex = -1;
                                }
                            }

                            if (string.IsNullOrWhiteSpace(operatorText))
                            {

                                //搜索到了结尾没有出现逻辑操作符，那么从当前到结尾的都是属性名
                                property = fullText.Substring(token.BeginIndex);
                            }
                            else
                            {
                                property = fullText.Substring(token.BeginIndex, operatorIndex - token.BeginIndex);
                            }
                            i = i - token.Text.Length + property.Length;
                            token.Text = property;
                            token.Type = State.PropertyName;
                            if (properties.Contains(property))
                            {
                                token.IsCurrent =
                                    (position + 1 >= token.BeginIndex && position < i) && !IsEOL(position);
                            }
                            else
                            {
                                token.IsCurrent = position + 1 >= token.BeginIndex && position <= i;
                            }

                            currentState = State.CalcOperator;
                            propertyDescription.Name = token.Text;
                            methodDescription.Tokens.Add(token);
                            token = new Token()
                            {
                                BeginIndex = i + 1,
                                Text = string.Empty
                            };
                            if (IsEOL(i) && properties.Contains(property))
                            {
                                token.Type = State.Operator;
                                token.IsCurrent = position == i;
                                methodDescription.Tokens.Add(token);
                                methodDescription.Properties.Add(propertyDescription);
                                break;
                            }

                            if (!string.IsNullOrWhiteSpace(operatorText))
                            {
                                i += operatorText.Length;
                                token.IsCurrent = (position + 1 >= token.BeginIndex && position < i) && !IsEOL(position);
                                token.Text = operatorText;
                                if (CALCOPERATORS.Contains(operatorText))
                                {
                                    currentState = State.LogicOperator;
                                    propertyDescription.BinaryOperator = operatorText;
                                    token.Type = State.CalcOperator;
                                    if (IsEOL(i))
                                    {
                                        methodDescription.Properties.Add(propertyDescription);
                                    }
                                    methodDescription.Tokens.Add(token);
                                    token = new Token()
                                    {
                                        BeginIndex = i + 1,
                                        Type = currentState,
                                        Text = string.Empty
                                    };
                                    if (IsEOL(i))
                                    {
                                        token.IsCurrent = IsEOL(position);
                                        token.Type = State.LogicOperator;
                                        methodDescription.Tokens.Add(token);
                                    }
                                }
                                else if (LOGICOPERATORS.Contains(operatorText))
                                {
                                    currentState = State.PropertyName;
                                    propertyDescription.LogicOperator = operatorText;
                                    token.Type = State.LogicOperator;
                                    methodDescription.Properties.Add(propertyDescription);
                                    methodDescription.Tokens.Add(token);
                                    token = new Token()
                                    {
                                        BeginIndex = i + 1,
                                        Type = currentState,
                                        Text = string.Empty
                                    };
                                    if (IsEOL(i))
                                    {
                                        token.IsCurrent = IsEOL(position);
                                        token.Type = State.PropertyName;
                                        methodDescription.Tokens.Add(token);
                                        methodDescription.Properties.Add(propertyDescription);
                                    }
                                }
                                else if (IsEOL(i))
                                {
                                    token.IsCurrent = position + 1 >= token.BeginIndex || IsEOL(position);
                                    token.Type = State.Operator;
                                    methodDescription.Tokens.Add(token);
                                    methodDescription.Properties.Add(propertyDescription);
                                }
                            }
                            else if (IsEOL(i))
                            {
                                token.IsCurrent = position >= token.BeginIndex - token.Text.Length;
                                methodDescription.Properties.Add(propertyDescription);
                            }
                        }
                        //将当前字符串拿去操作符列表搜索，如果存在，则当前已扫描到一个操作符
                        else if (CALCOPERATORS.Contains(token.Text))
                        {
                            propertyToken = CreateEmptyPropertyToken(position, i, token);
                            methodDescription.Tokens.Add(propertyToken);
                            var possibleOperators = CALCOPERATORS.Where(x => x.StartsWith(token.Text)).ToList();
                            var operatorText = possibleOperators.FirstOrDefault(x => token.BeginIndex + x.Length <= fullText.Length &&
                                                                                  fullText.Substring(token.BeginIndex, x.Length) == x);
                            i = i - token.Text.Length + operatorText.Length;
                            currentState = State.LogicOperator;

                            propertyDescription.BinaryOperator = token.Text;
                            token = new Token()
                            {
                                Type = State.CalcOperator,
                                BeginIndex = token.BeginIndex,
                                IsCurrent = (position + 1 >= token.BeginIndex - 1 && position < i) && !IsEOL(position),
                                Text = token.Text
                            };
                            methodDescription.Tokens.Add(token);
                            token = new Token()
                            {
                                BeginIndex = i + 1,
                                Text = string.Empty
                            };
                            if (IsEOL(i))
                            {
                                token.IsCurrent = position >= token.BeginIndex || IsEOL(position);
                                token.Type = State.LogicOperator;
                                methodDescription.Tokens.Add(token);
                                methodDescription.Properties.Add(propertyDescription);
                            }
                        }
                        //同理
                        else if (LOGICOPERATORS.Contains(token.Text))
                        {
                            propertyToken = CreateEmptyPropertyToken(position, i, token);
                            methodDescription.Tokens.Add(propertyToken);

                            var possibleOperators = LOGICOPERATORS.Where(x => x.StartsWith(token.Text)).ToList();
                            var operatorText = possibleOperators.FirstOrDefault(x => token.BeginIndex + x.Length <= fullText.Length &&
                                                                                     fullText.Substring(token.BeginIndex, x.Length) == x);
                            i = i - token.Text.Length + operatorText.Length;
                            currentState = State.PropertyName;

                            propertyDescription.LogicOperator = token.Text;
                            token = new Token()
                            {
                                Type = State.LogicOperator,
                                BeginIndex = token.BeginIndex,
                                IsCurrent = (position + 1 >= token.BeginIndex - 1 && position < i) && !IsEOL(position),
                                Text = token.Text
                            };
                            methodDescription.Tokens.Add(token);
                            methodDescription.Properties.Add(propertyDescription);
                            propertyDescription = new PropertyDescription();
                            token = new Token()
                            {
                                Type = State.PropertyName,
                                BeginIndex = i + 1,
                                Text = string.Empty
                            };
                            if (IsEOL(i))
                            {
                                token.IsCurrent = position == i;
                                methodDescription.Tokens.Add(token);
                                methodDescription.Properties.Add(propertyDescription);
                            }
                        }
                        else if (IsEOL(i))
                        {
                            token.IsCurrent = position >= token.BeginIndex - token.Text.Length;
                            propertyDescription.Name = token.Text;
                            methodDescription.Tokens.Add(token);
                            methodDescription.Properties.Add(propertyDescription);
                        }
                        else
                        {
                            //什么都不是，同时也不是结尾，有可能这个属性名本身就是错的，需要向前搜索，看是否有逻辑操作符
                            if (OPERATORS.Any(x => i + x.Length <= fullText.Length && fullText.Substring(i, x.Length) == x))
                            {
                                continue;
                            }
                            var operatorInfo = FindNextOperatorTokenUntilEnd(i + 1);
                            if (operatorInfo == null)
                            {
                                break;
                            }
                            var operatorText = operatorInfo.Text;

                            token.Type = State.PropertyName;
                            var operatorIndex = operatorInfo.BeginIndex;
                            token.Text = fullText.Substring(token.BeginIndex, operatorIndex - token.BeginIndex);
                            if (string.IsNullOrWhiteSpace(token.Text))
                            {
                                token.BeginIndex = -1;
                                token.IsCurrent = position + 1 == i;
                                i = token.BeginIndex + token.Text.Length - 1;
                                methodDescription.Tokens.Add(token);
                            }
                            else if (CALCOPERATORS.Contains(token.Text))
                            {
                                //中间的这个是一个操作符，补上空缺的Token
                                propertyToken = CreateEmptyPropertyToken(position, i, token);
                                methodDescription.Tokens.Add(propertyToken);
                                token.Type = State.CalcOperator;
                                propertyDescription.BinaryOperator = token.Text;
                                token.IsCurrent = position + 1 >= token.BeginIndex &&
                                                  position + 1 < token.BeginIndex + token.Text.Length;
                                methodDescription.Tokens.Add(token);
                                i = token.BeginIndex + token.Text.Length - 1;
                                token = new Token()
                                {
                                    BeginIndex = i + 1,
                                    Text = string.Empty,
                                    Type = State.LogicOperator
                                };
                            }
                            else
                            {
                                //中间的这个是一个属性名
                                propertyDescription.Name = token.Text;
                                token.IsCurrent = position + 1 >= token.BeginIndex &&
                                                  position + 1 < token.BeginIndex + token.Text.Length;
                                i = token.BeginIndex + token.Text.Length - 1;
                                methodDescription.Tokens.Add(token);
                            }

                            token = new Token()
                            {
                                BeginIndex = i + 1,
                                Text = operatorText,
                            };
                            token.IsCurrent = position + 1 >= token.BeginIndex &&
                                                                  position + 1 < token.BeginIndex + token.Text.Length &&
                                                    !IsEOL(position);
                            if (CALCOPERATORS.Contains(operatorText))
                            {
                                currentState = State.LogicOperator;
                                token.Type = State.CalcOperator;
                                propertyDescription.BinaryOperator = operatorText;
                            }
                            else if (LOGICOPERATORS.Contains(operatorText))
                            {
                                token.Type = State.LogicOperator;
                                propertyDescription.LogicOperator = operatorText;
                                currentState = State.PropertyName;
                                methodDescription.Properties.Add(propertyDescription);
                                propertyDescription = new PropertyDescription();
                            }
                            else if (ORDERBYOPERATORS.Contains(operatorText))
                            {
                                token.Type = State.OrderBy;
                                orderBy = new PropertyOrderBy();
                                currentState = State.OrderBy;
                                methodDescription.Properties.Add(propertyDescription);
                                propertyDescription = new PropertyDescription();
                            }
                            methodDescription.Tokens.Add(token);
                            i += token.Text.Length;
                            token = new Token()
                            {
                                BeginIndex = i + 1,
                                Text = string.Empty,
                                Type = currentState == State.OrderBy ? State.PropertyName : currentState
                            };
                            if (IsEOL(i))
                            {
                                token.IsCurrent = IsEOL(position);
                                methodDescription.Tokens.Add(token);
                                if (currentState == State.OrderBy)
                                {
                                    token.Type = State.PropertyName;
                                    methodDescription.PropertyOrderBys.Add(orderBy);
                                }
                                else
                                {
                                    methodDescription.Properties.Add(propertyDescription);
                                }
                            }
                        }

                        break;
                    case State.CalcOperator:
                        token.Text += c.ToString();
                        token.Type = currentState;
                        if (CALCOPERATORS.Contains(token.Text))
                        {
                            var possibleOperators = CALCOPERATORS.Where(x => x.StartsWith(token.Text)).ToList();
                            var operatorText = possibleOperators.FirstOrDefault(x => token.BeginIndex + x.Length <= fullText.Length &&
                                                                                     fullText.Substring(token.BeginIndex, x.Length) == x);
                            i = i - token.Text.Length + operatorText.Length;
                            token.Text = operatorText;
                            token.IsCurrent = (position >= token.BeginIndex && position < i) ||
                                              (IsEOL(i) && position == i);
                            methodDescription.Tokens.Add(token);
                            propertyDescription.BinaryOperator = token.Text;
                            if (IsEOL(i))
                            {
                                methodDescription.Properties.Add(propertyDescription);
                                break;
                            }
                            token = new Token();
                            token.BeginIndex = i + 1;
                            currentState = State.LogicOperator;
                        }
                        else if (IsEOL(i))
                        {
                            token.IsCurrent = position >= token.BeginIndex - token.Text.Length;
                            methodDescription.Tokens.Add(token);
                            methodDescription.Properties.Add(propertyDescription);
                        }
                        else
                        {
                            var operatorText = LOGICOPERATORS.FirstOrDefault(x => i + 1 + x.Length <= fullText.Length &&
                                                                                     fullText.Substring(i + 1, x.Length) == x);
                            if (!string.IsNullOrWhiteSpace(operatorText))
                            {
                                //在一个属性和一个逻辑操作符之间有一个不正确的计算操作符
                                token.IsCurrent = position >= i - token.Text.Length + 1 && position < i;
                                methodDescription.Tokens.Add(token);
                                token = new Token()
                                {
                                    BeginIndex = i + 1,
                                    Type = State.LogicOperator,
                                    Text = operatorText,
                                    IsCurrent = position >= i + token.Text.Length && position < i + operatorText.Length
                                };
                                propertyDescription.LogicOperator = operatorText;
                                methodDescription.Properties.Add(propertyDescription);
                                methodDescription.Tokens.Add(token);
                            }
                        }
                        break;
                    case State.LogicOperator:
                        token.Text += c.ToString();
                        token.Type = currentState;
                        if (LOGICOPERATORS.Contains(token.Text))
                        {
                            var possibleOperators = LOGICOPERATORS.Where(x => x.StartsWith(token.Text)).ToList();
                            var operatorText = possibleOperators.FirstOrDefault(x => token.BeginIndex + x.Length <= fullText.Length &&
                                                                                     fullText.Substring(token.BeginIndex, x.Length) == x);
                            i = i - token.Text.Length + operatorText.Length;
                            token.Text = operatorText;
                            token.IsCurrent = (position + 1 >= token.BeginIndex && position < i) && !IsEOL(position);
                            methodDescription.Tokens.Add(token);
                            propertyDescription.LogicOperator = token.Text;
                            methodDescription.Properties.Add(propertyDescription);
                            propertyDescription = new PropertyDescription();
                            currentState = State.PropertyName;
                            token = new Token
                            {
                                Type = State.PropertyName,
                                BeginIndex = i + 1,
                                Text = string.Empty
                            };
                            if (IsEOL(i))
                            {
                                token.IsCurrent = position == i;
                                methodDescription.Tokens.Add(token);
                            }
                        }
                        else if (IsEOL(i))
                        {
                            propertyDescription.LogicOperator = token.Text;
                            methodDescription.Properties.Add(propertyDescription);
                            propertyDescription = new PropertyDescription();
                            token.IsCurrent = (position >= token.BeginIndex - token.Text.Length && position < i) ||
                                                                          (IsEOL(i) && position == i);
                            methodDescription.Tokens.Add(token);
                        }
                        break;
                    case State.OrderBy:
                        orderBy = new PropertyOrderBy();
                        var directionToken = FindNextToken(i, SORTDIRECTIONOPERATORS);
                        if (directionToken != null)
                        {
                            //OrderByDesc，根本没写属性
                            directionToken.Type = State.OrderByDirection;
                            orderBy.Direction = directionToken.Text;
                            methodDescription.PropertyOrderBys.Add(orderBy);
                            directionToken.IsCurrent = IsCurrent(directionToken, position);
                            methodDescription.Tokens.Add(new Token()
                            {
                                BeginIndex = i,
                                IsCurrent = position + 1 == i,
                                Text = string.Empty,
                                Type = State.PropertyName
                            });
                            methodDescription.Tokens.Add(directionToken);
                            i += directionToken.Text.Length - 1;
                            var andToken = FindNextLogicToken(i, "And");
                            if (andToken != null)
                            {
                                andToken.IsCurrent = IsCurrent(andToken, position);
                                methodDescription.Tokens.Add(andToken);
                                i += andToken.Text.Length - 1;
                                CreateEmptyTokenIfEOL(position, i, State.PropertyName);
                                break;
                            }

                            if (CreateEmptyTokenIfEOL(position, i, State.LogicOperator))
                            {
                                break;
                            }

                        }
                        propertyToken = FindNextProperty(i);
                        if (propertyToken == null)
                        {
                            token = new Token();
                            token.BeginIndex = i;
                            directionToken = FindNextTokenUntilEnd(i, SORTDIRECTIONOPERATORS);
                            if (directionToken != null)
                            {
                                //OrderByasdfsaasdfDesc
                                directionToken.Type = State.OrderByDirection;
                                token.Text = fullText.Substring(i, directionToken.BeginIndex - token.BeginIndex);
                                orderBy.Property = token.Text;
                                i += token.Text.Length - 1;
                                token.Type = State.PropertyName;
                                token.IsCurrent = position + 1 >= token.BeginIndex && position < i;
                                methodDescription.Tokens.Add(token);
                                directionToken.IsCurrent = IsCurrent(directionToken, position);
                                methodDescription.Tokens.Add(directionToken);
                                i += directionToken.Text.Length;
                                orderBy.Direction = directionToken.Text;
                                methodDescription.PropertyOrderBys.Add(orderBy);
                                token = new Token();
                                var andToken = FindNextLogicToken(i + 1, "And");
                                if (andToken != null)
                                {
                                    andToken.IsCurrent = IsCurrent(andToken, position);
                                    i += andToken.Text.Length;
                                    methodDescription.Tokens.Add(andToken);
                                    if (CreateEmptyTokenIfEOL(position, i, State.PropertyName))
                                    {
                                        methodDescription.PropertyOrderBys.Add(new PropertyOrderBy());
                                    }
                                    break;
                                }
                                else if (IsEOL(i) && CreateEmptyTokenIfEOL(position, i, State.LogicOperator))
                                {
                                    methodDescription.PropertyOrderBys.Add(new PropertyOrderBy());
                                    break;
                                }
                                else
                                {
                                    Debugger.Break();
                                }
                            }
                            else
                            {
                                var andToken = FindNextLogicToken(i, "And");
                                if (andToken != null)
                                {
                                    token.Text = fullText.Substring(i, andToken.BeginIndex - token.BeginIndex);
                                    orderBy.Property = token.Text;
                                    i += token.Text.Length - 1;
                                    token.Text = fullText.Substring(i);
                                    token.Type = State.PropertyName;
                                    token.IsCurrent = position + 1 >= token.BeginIndex;
                                    methodDescription.Tokens.Add(token);
                                    token = new Token()
                                    {
                                        BeginIndex = i + 1,
                                        IsCurrent = position == i,
                                        Text = string.Empty,
                                        Type = State.OrderByDirection
                                    };
                                    methodDescription.Tokens.Add(token);
                                    andToken.IsCurrent = IsCurrent(andToken, position);
                                    methodDescription.Tokens.Add(andToken);
                                    i += andToken.Text.Length;
                                    if (CreateEmptyTokenIfEOL(position, i, State.PropertyName))
                                    {
                                        methodDescription.PropertyOrderBys.Add(new PropertyOrderBy());
                                        break;
                                    }
                                }
                                else if (IsEOL(i))
                                {
                                    token.Text = fullText.Substring(i);
                                    token.IsCurrent = position + 1 >= token.BeginIndex;
                                    token.Type = State.PropertyName;
                                    orderBy.Property = token.Text;
                                    methodDescription.Tokens.Add(token);
                                    methodDescription.PropertyOrderBys.Add(orderBy);
                                }
                                else
                                {
                                    Debugger.Break();
                                }
                            }
                        }
                        else
                        {
                            //OrderById这样，存在这个属性，但要后面可能有其他字符
                            directionToken = FindNextToken(i + propertyToken.Text.Length, SORTDIRECTIONOPERATORS);
                            if (directionToken == null)
                            {
                                if (IsEOL(i))
                                {
                                    //OrderById这样，没有其他字符
                                    token.Text = propertyToken.Text;
                                    token.IsCurrent = position + 1 >= token.BeginIndex && position < i && !IsEOL(position);
                                }
                                else
                                {
                                    //OrderByIdxxxxx这样
                                    //OrderById1Desc这样
                                    directionToken = FindNextTokenUntilEnd(i + propertyToken.Text.Length,
                                        SORTDIRECTIONOPERATORS);
                                    var nextPropertyToken =
                                        FindNextTokenUntilEnd(i + propertyToken.Text.Length, properties);
                                    if (directionToken == null)
                                    {
                                        var andToken = FindNextTokenUntilEnd(i, "And");
                                        if (andToken == null)
                                        {
                                            //OrderByIdxxxxx这样，有其他字符，但这个字符不正确，此时Id作为属性，其他字符作为操作符
                                            propertyToken.IsCurrent = IsCurrent(propertyToken, position);
                                            methodDescription.Tokens.Add(propertyToken);
                                            orderBy.Property = propertyToken.Text;
                                            i += propertyToken.Text.Length;
                                            token = new Token()
                                            {
                                                BeginIndex = i,
                                                IsCurrent = position + 1 >= i,
                                                Text = fullText.Substring(i),
                                                Type = State.Operator
                                            };
                                            methodDescription.Tokens.Add(token);

                                            methodDescription.PropertyOrderBys.Add(orderBy);
                                            break;
                                        }
                                        //OrderByIdxxxxxAnd这样，后面有逻辑操作符
                                        propertyToken.Text = fullText.Substring(propertyToken.BeginIndex,
                                            andToken.BeginIndex - propertyToken.BeginIndex);
                                        propertyToken.IsCurrent = IsCurrent(propertyToken, position);
                                        orderBy.Property = propertyToken.Text;
                                        methodDescription.Tokens.Add(propertyToken);
                                        i += propertyToken.Text.Length;
                                        methodDescription.PropertyOrderBys.Add(orderBy);
                                        methodDescription.Tokens.Add(new Token()
                                        {
                                            BeginIndex = i,
                                            IsCurrent = position + 1 == andToken.BeginIndex,
                                            Text = string.Empty,
                                            Type = State.OrderByDirection
                                        });
                                        andToken.Type = State.LogicOperator;
                                        andToken.IsCurrent = IsCurrent(andToken, position);
                                        methodDescription.Tokens.Add(andToken);
                                        i += andToken.Text.Length - 1;
                                        if (CreateEmptyTokenIfEOL(position, i, State.PropertyName))
                                        {
                                            methodDescription.PropertyOrderBys.Add(new PropertyOrderBy());
                                            break;
                                        }
                                    }
                                    else if (nextPropertyToken == null || nextPropertyToken.BeginIndex > directionToken.BeginIndex)
                                    {
                                        directionToken.Type = State.OrderByDirection;
                                        propertyToken.Text = fullText.Substring(propertyToken.BeginIndex,
                                            directionToken.BeginIndex - propertyToken.BeginIndex);
                                        propertyToken.IsCurrent = IsCurrent(propertyToken, position);
                                        methodDescription.Tokens.Add(propertyToken);
                                        orderBy.Property = propertyToken.Text;
                                        i += propertyToken.Text.Length - 1;
                                        if (IsEOL(i + directionToken.Text.Length))
                                        {
                                            directionToken.IsCurrent = IsCurrent(directionToken, position);
                                            methodDescription.Tokens.Add(directionToken);
                                            i += directionToken.Text.Length;
                                            //OrderById1Desc
                                            orderBy.Direction = directionToken.Text;
                                            methodDescription.PropertyOrderBys.Add(orderBy);
                                            methodDescription.Tokens.Add(new Token()
                                            {
                                                BeginIndex = i + 1,
                                                Type = State.LogicOperator,
                                                Text = string.Empty,
                                                IsCurrent = position == i
                                            });
                                            break;
                                        }
                                        //有方向操作符，下一个应该是逻辑操作符
                                        var andToken = FindNextLogicToken(i, "And");
                                        if (andToken == null)
                                        {
                                            //一直往后，是否会有逻辑操作符
                                            andToken = FindNextTokenUntilEnd(i, "And");
                                            if (andToken == null)
                                            {
                                                //OrderById1Descadfasdfp这样，后面的字符全作为排序处理
                                                directionToken.Text = fullText.Substring(directionToken.BeginIndex);
                                                directionToken.IsCurrent = position + 1 >= directionToken.BeginIndex && position + 1 < directionToken.BeginIndex + directionToken.Text.Length;
                                                methodDescription.Tokens.Add(directionToken);
                                                orderBy.Direction = directionToken.Text;
                                                methodDescription.PropertyOrderBys.Add(orderBy);
                                                i += directionToken.Text.Length - 1;
                                                break;
                                            }

                                            //OrderById1DescadfasdfpAnd这样，And之前的字符全作为排序处理
                                            directionToken.Text = fullText.Substring(directionToken.BeginIndex, andToken.BeginIndex - directionToken.BeginIndex);
                                            directionToken.IsCurrent = IsCurrent(directionToken, position);
                                            methodDescription.Tokens.Add(directionToken);
                                            i += directionToken.Text.Length - 1;
                                            andToken.Type = State.LogicOperator;
                                            andToken.IsCurrent = IsCurrent(andToken, position);
                                            methodDescription.Tokens.Add(andToken);
                                            i += andToken.Text.Length - 1;
                                            if (IsEOL(i))
                                            {
                                                methodDescription.Tokens.Add(new Token()
                                                {
                                                    BeginIndex = i,
                                                    IsCurrent = position + 1 == i,
                                                    Text = string.Empty,
                                                    Type = State.PropertyName
                                                });
                                                break;
                                            }

                                            token = new Token()
                                            {
                                                BeginIndex = i,
                                                Text = string.Empty,
                                                Type = State.PropertyName
                                            };
                                        }
                                        else
                                        {
                                            //OrderById1DescAnd
                                            token.Text = directionToken.Text;
                                            token.Type = State.OrderByDirection;
                                            token.IsCurrent = IsCurrent(directionToken, position);
                                            andToken.IsCurrent = IsCurrent(andToken, position);
                                            methodDescription.Tokens.Add(token);
                                            methodDescription.Tokens.Add(andToken);
                                            i += token.Text.Length + andToken.Text.Length - 2;
                                            token = new Token()
                                            {
                                                BeginIndex = i,
                                                Text = string.Empty,
                                                Type = State.PropertyName
                                            };
                                            if (IsEOL(i))
                                            {
                                                methodDescription.Tokens.Add(token);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        directionToken.BeginIndex = propertyToken.BeginIndex + propertyToken.Text.Length;
                                        directionToken.Text = string.Empty;
                                        directionToken.Type = State.OrderByDirection;
                                        propertyToken.IsCurrent = IsCurrent(propertyToken, position);
                                        methodDescription.Tokens.Add(propertyToken);
                                        orderBy.Property = propertyToken.Text;
                                        orderBy.Direction = directionToken.Text;
                                        methodDescription.Tokens.Add(directionToken);
                                        methodDescription.PropertyOrderBys.Add(orderBy);
                                        i += propertyToken.Text.Length - 1 + directionToken.Text.Length;
                                        var andToken = FindNextLogicToken(i + 1, "And");
                                        if (andToken != null)
                                        {
                                            directionToken.IsCurrent = position + 1 == andToken.BeginIndex;
                                            andToken.IsCurrent = IsCurrent(andToken, position);
                                            methodDescription.Tokens.Add(andToken);
                                            i += andToken.Text.Length;
                                            if (CreateEmptyTokenIfEOL(position, i, State.PropertyName))
                                            {
                                                methodDescription.PropertyOrderBys.Add(new PropertyOrderBy());
                                                break;
                                            }
                                        }
                                        if (CreateEmptyTokenIfEOL(position, i, State.LogicOperator))
                                        {
                                            break;
                                        }
                                    }
                                }
                                if (IsEOL(i))
                                {
                                    token = new Token()
                                    {
                                        BeginIndex = i,
                                        IsCurrent = position + 1 == i,
                                        Text = string.Empty,
                                        Type = State.LogicOperator
                                    };
                                    methodDescription.Tokens.Add(token);
                                }
                            }
                            else
                            {
                                //OrderByIdDesc，找到了排序方向
                                orderBy.Property = propertyToken.Text;
                                var andToken = FindNextLogicToken(i, "And");
                                if (andToken == null)
                                {
                                    token.BeginIndex = propertyToken.BeginIndex;
                                    if (IsEOL(i))
                                    {
                                        //OrderByIdDesc，找到了排序方向，没有下一个
                                        token.Text = orderBy.Property;
                                        token.IsCurrent = position + 1 >= token.BeginIndex;
                                        methodDescription.Tokens.Add(token);
                                    }
                                    else
                                    {
                                        //OrderByIdDesc，找到了排序方向，有下一个
                                        token.Text = orderBy.Property;
                                        token.IsCurrent = IsCurrent(token, position);
                                        methodDescription.Tokens.Add(token);
                                        i += orderBy.Property.Length - 1;
                                        token = new Token()
                                        {
                                            BeginIndex = i + 1
                                        };
                                        //可能有and，也可能没有
                                        andToken = FindNextTokenUntilEnd(i, "And");
                                        if (andToken == null)
                                        {
                                            //OrderByIdDescAasfd
                                            //此时Desc视为排序规则，Aasfd视为And操作符
                                            directionToken.IsCurrent = IsCurrent(directionToken, position);
                                            directionToken.Type = State.OrderByDirection;
                                            methodDescription.Tokens.Add(directionToken);
                                            i += directionToken.Text.Length;
                                            token.BeginIndex = i + 1;
                                            token.Type = State.LogicOperator;
                                            token.Text = fullText.Substring(token.BeginIndex);
                                            token.IsCurrent = position + 1 >= token.BeginIndex;
                                            methodDescription.Tokens.Add(token);
                                            orderBy.Direction = directionToken.Text;
                                            i += token.Text.Length;
                                            //token = new Token()
                                            //{
                                            //    BeginIndex = i,
                                            //    Text = string.Empty
                                            //};
                                            //if (IsEOL(i))
                                            //{
                                            //    token.Type = State.LogicOperator;
                                            //    token.IsCurrent = IsEOL(position);
                                            //    methodDescription.Tokens.Add(token);
                                            //    methodDescription.PropertyOrderBys.Add(orderBy);
                                            //    break;
                                            //}
                                            methodDescription.PropertyOrderBys.Add(orderBy);
                                        }
                                        else
                                        {
                                            //OrderByIdDescAasfdAnd
                                            //此时DescAasfd视为排序顺序
                                            token.Type = State.OrderByDirection;
                                            token.Text = fullText.Substring(token.BeginIndex,
                                                andToken.BeginIndex - token.BeginIndex);
                                            orderBy.Direction = token.Text;
                                            i += token.Text.Length;
                                            if (IsEOL(i))
                                            {
                                                token.IsCurrent = position + 1 >= token.BeginIndex;
                                            }
                                            else
                                            {
                                                token.IsCurrent = IsCurrent(token, position);
                                            }
                                            methodDescription.Tokens.Add(token);
                                            andToken.Type = State.LogicOperator;
                                            andToken.IsCurrent = IsCurrent(andToken, position);
                                            i += andToken.Text.Length;
                                            methodDescription.Tokens.Add(andToken);
                                            methodDescription.PropertyOrderBys.Add(orderBy);
                                            if (CreateEmptyTokenIfEOL(position, i, State.PropertyName))
                                            {
                                                methodDescription.PropertyOrderBys.Add(new PropertyOrderBy());
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    //OrderByIdDescAnd
                                    propertyToken.IsCurrent = IsCurrent(propertyToken, position);
                                    methodDescription.Tokens.Add(propertyToken);
                                    directionToken.IsCurrent = IsCurrent(directionToken, position);
                                    methodDescription.Tokens.Add(directionToken);
                                    andToken.IsCurrent = IsCurrent(andToken, position);
                                    methodDescription.Tokens.Add(andToken);
                                    i = andToken.BeginIndex + andToken.Text.Length;
                                    if (IsEOL(i))
                                    {
                                        methodDescription.Tokens.Add(new Token()
                                        {
                                            BeginIndex = i,
                                            IsCurrent = position == i,
                                            Text = string.Empty,
                                            Type = State.PropertyName
                                        });
                                        methodDescription.PropertyOrderBys.Add(new PropertyOrderBy());
                                    }
                                }
                            }
                        }
                        break;
                }
            }

            return methodDescription;
        }

        private bool CreateEmptyTokenIfEOL(int position, int i, State type)
        {
            if (IsEOL(i))
            {
                methodDescription.Tokens.Add(new Token()
                {
                    BeginIndex = i + 1,
                    IsCurrent = position == i,
                    Text = string.Empty,
                    Type = type
                });
                return true;
            }

            return false;
        }

        /// <summary>
        /// 获取指定当前光标是否在指定Token当中
        /// </summary>
        /// <param name="token"></param>
        /// <param name="position"></param>
        /// <returns></returns>
        bool IsCurrent(Token token, int position)
        {
            return position + 1 >= token.BeginIndex && position + 1 < token.BeginIndex + token.Text.Length &&
                   !IsEOL(position);
        }

        private Token CreateEmptyPropertyToken(int position, int i, Token token)
        {
            var propertyToken = new Token()
            {
                Type = State.PropertyName,
                BeginIndex = -1,
                Text = string.Empty,
                IsCurrent = position == i - token.Text.Length
            };
            return propertyToken;
        }

        private Token FindNextTextUntilEnd(int i, IEnumerable<string> texts)
        {
            var token = new Token();
            var operatorText = string.Empty;
            var operatorIndex = 0;
            for (var j = i; j < fullText.Length; j++)
            {
                operatorText =
                    texts.FirstOrDefault(x => x.Length + j <= fullText.Length && fullText.Substring(j, x.Length) == x);
                if (string.IsNullOrWhiteSpace(operatorText))
                {
                    continue;
                }

                operatorIndex = j;
                break;
            }

            if (string.IsNullOrWhiteSpace(operatorText))
            {
                return null;
            }

            return new Token()
            {
                BeginIndex = operatorIndex,
                Text = operatorText
            };
        }
        private Token FindNextToken(int i, params string[] texts)
        {
            var text = texts.FirstOrDefault(x => i + x.Length <= fullText.Length && fullText.Substring(i, x.Length) == x);

            if (string.IsNullOrWhiteSpace(text))
            {
                return null;
            }

            return new Token()
            {
                BeginIndex = i,
                Text = text
            };
        }

        private Token FindNextLogicToken(int i, params string[] texts)
        {
            var token = FindNextToken(i, texts);
            if (token == null)
            {
                return null;
            }

            token.Type = State.LogicOperator;
            return token;
        }

        private Token FindNextOperatorTokenUntilEnd(int i)
        {
            var operatorToken = FindNextTextUntilEnd(i, OPERATORS);
            if (operatorToken == null)
            {
                return null;
            }
            operatorToken.Type = CALCOPERATORS.Contains(operatorToken.Text) ? State.CalcOperator : State.LogicOperator;
            return operatorToken;
        }

        private Token FindNextTokenUntilEnd(int i, params string[] texts)
        {
            var operatorToken = FindNextTextUntilEnd(i, texts);
            return operatorToken;
        }

        private Token FindNextProperty(int i)
        {
            var property = FindNextToken(i, properties);
            if (property == null)
            {
                return null;
            }
            property.Type = State.PropertyName;
            return property;
        }
    }
}
